<?php
/**
 * XmuSlh, web platform of Student Association Federation of Xiamen University
 * 
 * Rewritting one or some pages of StatusNet. 
 * To get the original version, please visit <http://status.net/>.
 * 
 * StatusNet, the distributed open-source microblogging tool
 * 
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * A router class to serve the router mapping. 
 * Direct the route to the relative class.
 * 
 * 路径映射, 将http://../groups等的URL映射到相应的Action, 并且对参数进行解释.
 * 
 * @author		ytukie <ytukie@gmail.com>
 * @version		0.1
 */

// XmuSlh only.
if ( !defined('XMUSLH')) { exit(1); }

// Requirements.
require_once INSTALLDIR . '/extlib/Net/URL/Mapper.php';

// TODO: What is the meaning?
class XmuSlh_URL_Mapper extends Net_URL_Mapper
{
	private static $_singleton = null;
	private $_actionToPath = array();
	
	private function __construct()
	{
	}
	
	public static function getInstance($id = '__default__')
	{
		if (empty(self::$_singleton)) {
			self::$_singleton = new XmuSlh_URL_Mapper();
		}
		
		return self::$_singleton;
	}
	
	public function connect($path, 
							$defaults = array(), 
							$rules = array())
	{
		$result = null;
		$action = null;
		
		// Ignore the event: 'StartConnectPath'
		$result = parent::connect($path, $defaults, $result);
		if (array_key_exists('action', $defaults)) {
			$actoin = $defaults['action'];
		} else if (array_key_exists('action', $rules)) {
			$action = $rules['action'];
		}
		
		$this->_mapAction($action, $result);
		
		return $result;
	}
	
	protected function _mapAction($action, $path)
	{
		if ( !array_key_exists($action, $this->_actionToPath)) {
			$this->_actionToPath[$action] = array();
		}
		$this->actionToPath[$action][] = $path;
		
		return;
	}
	
	public function generate($values = array(), 
							 $qstring = array(), 
							 $anchor = '')
	{
		if ( !array_key_exists('action', $values)) {
			return parent::generate($values, $qstring, $anchor);
		}
		
		$action = $values['action'];

        if ( !array_key_exists($action, $this->_actionToPath)) {
            return parent::generate($values, $qstring, $anchor);
        }
		
		$oldPaths		= $this->paths;
		$this->paths	= $this->_actionToPath[$action];
		$result			= parent::generate($values, $qstring, $anchor);
		$this->paths	= $oldPaths;
		
		return $result;
	}
}

/**
 * URL Router
 * Cheap wrapper around Net_URL_Mapper.
 * 
 * TODO: The detals:
 */
class Router
{
	var $mapping = null;
	static $inst = null;
	/* static $bare = array('requesttoken', 'accesstoken', 'userauthorization',
                         'postnotice', 'updateprofile', 'finishremotesubscribe'); */

    const REGEX_TAG = '[^\/]+'; // [\pL\pN_\-\.]{1,64} better if we can do unicode regexes
    
    static function get()
    {
    	if ( !Router::$inst) {
    		Router::$inst = new Router();
    	}
    	return Router::$inst;
    }
    
    function __construct()
    {
    	if (empty($this->m)) {
// TODO: Use the cache!
			/*
    		if (!common_config('router', 'cache')) {
                $this->m = $this->initialize();
            } else {
                $k = self::cacheKey();
                $c = Cache::instance();
                $mapping = $c->get($k);
                if (!empty($mapping)) {
                    $this->m = $mapping;
                } else {
                    $this->m = $this->initialize();
                    $c->set($k, $this->m);
                }
            }
            */
    		
    		$this->mapping = $this->initialize();
    	}
    }
    
    /**
     * TODO: What is this?
     * 
     * Create a unique hashkey for the router.
     * 
     * The router's url map can change based on the version of the software
     * you're running and the plugins that are enabled. To avoid having bad routes
     * get stuck in the cache, the key includes a list of plugins and the software
     * version.
     */
    static function cacheKey()
    {
    	$parts = array('router');
    	
    	// Many router paths depend on this setting.
    	
    	/*
    	if (common_config('singleuser', 'enabled')) {
            $parts[] = '1user';
        } else {
            $parts[] = 'multi';
        }

        return Cache::codeKey(implode(':', $parts));
    	 */
    }
    
    /**
     * TODO: A lot of maps! Hah!
     * 
     * Initialize the mapper.
     */
    function initialize()
    {
    	$mapping = XmuSlh_URL_Mapper::getInstance();
    	
 		// A lot of maps!!!
 		/*********************************************Begin mapping*********************************/
    	
    	/*
    	// 1. One simple test without parameters.
    	$mapping->connect('micronews', array('action' => 'getinfo'));
    	$mapping->connect('info/get/', array('action' => 'getinfo'));
    	
    	// 2. One test with parameter.
    	$mapping->connect('info/:id', array('action' => 'first'), array('id' => '[0-9]+'));
    	
    	// 3. With parameters?
    	$mapping->connect('more/:name/:pass', array('action' => 'getinfo'));
    	
    	$mapping->connect('limit/:format', array('action' => 'getinfo'), 
    									   array('format' => '(xml|json|rss|atom|as)'));
    	*/
    	
    	$mapping->connect('getinfo', array('action' => 'getinfo'), 
    								 array('catalog' => 'all'));
    	$mapping->connect('getinfo/:catalog', array('action' => 'getinfo'),
    										  array('catalog' => '[0-9]+'));
    										  
    	// Database
    	$mapping->connect('database', array('action' => 'database'));
    	
    	/*********************************************End mapping***********************************/
    	//
    	
    	return $mapping;
    }
    
    /**
     * TODO: Logs and shows the exception.
     * 
     * Parse the url to the array includes action and arguments.
     */
    function map($path)
    {
    	try {
    		$match = $this->mapping->match($path);
    	} catch (Net_URL_Mapper_InvalidException $e) {
			// Logs and shows the exception.
			// TRANS: Client error on action trying to visit a non-existing page.
			/*
            $cac = new ClientErrorAction(_('Page not found.'), 404);
            $cac->showPage();
            */
    	}
    	
    	return $match;
    }
    
    /**
     * TODO: A little stuck.
     * Against the map!!
     * Bulid the router.
     */
    function build($action, 
    			   $args = null, 
    			   $params = null, 
    			   $fragment = null)
    {
    	$action_arg = array('action' => $action);
    	
    	if ($args) {
    		$args = array_merge($action_arg, $args);
    	} else {
    		$args = $action_arg;
    	}
    	
    	$url = $this->mapping->generate($args, $params, $fragment);
    	
    	// Copy from StatusNet below.
    	// Due to a bug in the Net_URL_Mapper code, the returned URL may
        // contain a malformed query of the form ?p1=v1?p2=v2?p3=v3. We
        // repair that here rather than modifying the upstream code...

        $qpos = strpos($url, '?');
        if ($qpos !== false) {
            $url = substr($url, 0, $qpos+1) .
                str_replace('?', '&', substr($url, $qpos+1));

            // @fixme this is a hacky workaround for http_build_query in the
            // lower-level code and bad configs that set the default separator
            // to &amp; instead of &. Encoded &s in parameters will not be
            // affected.
            $url = substr($url, 0, $qpos+1) .
                str_replace('&amp;', '&', substr($url, $qpos+1));

        }

        return $url;
    }
}

?>